/** ****************************************************************************
@defgroup UART UART
@ingroup BSP
@brief 串口驱动
@addtogroup UART
@{
*******************************************************************************/

#include "stdinc.h"


/// @brief UART 初始化
void UART_init(
	UARTPort *uart,
	uint32_t baudrate,
	USART_WordLength_TypeDef dataBits,
	USART_StopBits_TypeDef stopBits,
	USART_Parity_TypeDef parity )
{
    USART_Init2(uart, baudrate, dataBits, stopBits, parity, (USART_Mode_TypeDef)(USART_Mode_Rx|USART_Mode_Tx));
    USART_Cmd(uart, ENABLE);
}

/*
USART_FLAG_xxx/USART_IT_xxx:
   CTS:  CTS Change flag (not available for UART4 and UART5)
   LBD:  LIN Break detection flag
   TXE:  Transmit data register empty flag
   TC:   Transmission Complete flag
   RXNE: Receive data register not empty flag
   IDLE: Idle Line detection flag
   ORE:  OverRun Error flag
   NE:   Noise Error flag
   FE:   Framing Error flag
   PE:   Parity Error flag

 - IDLE, ORE, NE, FE, PE 中断清除方法：
   USART_GetITStatus()
   USART_ReceiveData()
*/
void UART_enableIrqRx(UARTPort *uart, bool sw) {
    // 启用接收数据中断
    USART_ITConfig(uart, USART_IT_RXNE, (FunctionalState)sw);
    USART_ITConfig(uart, USART_IT_ORE, 	(FunctionalState)sw);

    // 禁用其它中断
	USART_ITConfig(uart, USART_IT_IDLE, DISABLE);
	USART_ITConfig(uart, USART_IT_PE, 	DISABLE);
	USART_ITConfig(uart, USART_IT_ERR, 	DISABLE);
	USART_ITConfig(uart, USART_IT_NE, 	DISABLE);
	USART_ITConfig(uart, USART_IT_FE, 	DISABLE);
}

void UART_enableIrqTx(UARTPort *uart, bool sw) {
    USART_ITConfig(uart, USART_IT_TC, 	(FunctionalState)sw);
    USART_ITConfig(uart, USART_IT_TXE, 	(FunctionalState)sw);
}

/// @brief 接收中断
void UART_rxIRQHandler(UARTPort *uart, FUNC_void_u8_voidp onRxChar, void *cookie) {
	__IO uint32_t SR = uart->SR;
    __IO uint8_t ch = (uart->DR & 0xFF);

	if( (SR & USART_FLAG_PE) || (SR & USART_FLAG_FE) || (SR & USART_FLAG_NE) ) {
        // 数据错误，丢弃
        return;
    }

	if( (SR & USART_FLAG_ORE) || (SR & USART_FLAG_IDLE) ) {
        // 数据有效，继续
    }

	if(SR & USART_IT_RXNE) {
		if(onRxChar) { onRxChar(ch, cookie); }
    }
}

/// @brief 发送完成中断
void UART_txIRQHandler(UARTPort *uart, FUNC_void_voidp onTxComplete, void *cookie) {
	if( USART_GetITStatus(uart, USART_IT_TC) || USART_GetITStatus(uart, USART_IT_TXE) ) {
		if(onTxComplete) {
			onTxComplete(cookie);
		} else {
			// 如果没人管，则必须将TXE中断关闭，否则会不断地产生中断，导致系统down机
			UART_enableIrqTx(uart, false);
		}

		USART_ClearITPendingBit(uart, USART_IT_TC);
        USART_ClearITPendingBit(uart, USART_IT_TXE);
		return;
    }
}

/// 单字符输出
uint8_t UART_putc(UARTPort *uart, uint8_t ch) {
	// TXE：指数据已经放入输出移位寄存器，等待逐个bit传输
	// TXC：指数据已经在数据线上传输完成
    while (USART_GetFlagStatus(uart, USART_FLAG_TXE) == RESET);
    USART_SendData8(uart, ch);
	return ch;
}

/// 字符输入
uint8_t UART_getc(UARTPort *uart) {
	while (USART_GetFlagStatus(uart, USART_FLAG_RXNE) == RESET);
    return USART_ReceiveData8(uart);
}

size_t UART_send(UARTPort *uart, const void* data, size_t len) {
    uint8_t* bytes = (uint8_t*)data;
	if(!uart || !bytes || !len) { return 0; }

    for(; len>0; len--) {
        UART_putc(uart, *(bytes++));
    }
    return len;
}

int UART_receive(
	UARTPort *uart,	///< 串口句柄
	void* buff,				///< 接收数据缓冲区
	size_t buffSize,		///< 需要等待的最大响应字节数
	char *okFlag,          	///< OK情况下的响应数据标志
	char *failedFlag,    	///< 异常情况下的响应数据标志
	uint16_t timeoutTotal,      	///< 响应的总超时时间(ms)
	uint16_t timeoutInterval )  	///< 响应的数据字符间隔超时时间(ms)
{
	if(!uart || !buff || !buffSize) { return 0; }
	buffSize--;	// 留一个位置给尾部 '\0'

	// 同步接收数据时，应关闭接收中断，防止数据被中断服务程序读走了
	UART_enableIrqRx(uart, false);

	uint32_t start = osGetMs();
	uint32_t lastByteTime = 0;
	size_t len = 0;
	char *data = (char*)buff;
	data[0] = '\0';

	while(1) {
		if(USART_GetFlagStatus(uart, USART_FLAG_RXNE) != RESET) {
			// 收到数据
			data[len++] = USART_ReceiveData8(uart);
			lastByteTime = osGetMs();

			// 因缓冲区满中止
			if(len >= buffSize) { break; }
			// 因 OK 标志中止
			if(okFlag && strstr(data, okFlag)) { break; }
			// 因 Failed 标志中止
			if(failedFlag && strstr(data, failedFlag)) { break; }
		}

		// 因总超时中止
		if(timeoutTotal && (osElapsedMs(start) >= timeoutTotal)) { break; }
		// 因间隔超时中止
		if(timeoutInterval && lastByteTime && (osElapsedMs(lastByteTime) >= timeoutInterval)) { break; }
	}

	data[len] = '\0';
	printf(data);
	return len;
}


/// @}
